package br.ufrj.pesc.solr;

import java.net.MalformedURLException;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest.METHOD;
import org.apache.solr.client.solrj.response.FacetField;
import org.apache.solr.client.solrj.response.FacetField.Count;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrInputDocument;

import br.com.gpetec.mom.domain.entidades.Orientacao;
import br.com.gpetec.mom.domain.entidades.OrientacaoClassificador;
import br.com.gpetec.mom.domain.solr.documents.Documento;
import br.com.gpetec.utils.CalendarioUtils;


public class SearchSolrHandler extends SolrHandler implements
		ISearchHandler {

	public SearchSolrHandler() throws MalformedURLException {
		super();
	}

	public List<Documento> recuperaDocumentos(String queryString,
			Date dataInicio, Date dataFim) {
		List<Documento> retorno = new ArrayList<Documento>();

		Calendar calendarInicio = CalendarioUtils.getCalendario();
		calendarInicio.setTime(dataInicio);
		calendarInicio.add(Calendar.HOUR, 3);

		Calendar calendarFim = CalendarioUtils.getCalendario();
		calendarFim.setTime(dataFim);
		calendarFim.add(Calendar.HOUR, 3);

		query = new SolrQuery();
		query.setQuery(removeAcentos(queryString));
		query.setRows(1000);
		query.set("fq", "-orientacao:\"irrelevante\" tstamp:["
				+ formatadorDeData.format(calendarInicio.getTime()) + " TO "
				+ formatadorDeData.format(calendarFim.getTime()) + "]");

		try {
			QueryResponse rsp = server.query(query, METHOD.POST);

			if (rsp != null) {
				for (SolrDocument documento : rsp.getResults()) {
					Documento doc = new Documento(documento);
					retorno.add(doc);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return retorno;
	}

	public List<Documento> recuperaDocumentosTortaMencao(String nome,
			Date dataInicio, Date dataFim, List<String> fontes) {
		List<Documento> retorno = new ArrayList<Documento>();

		Calendar calendarInicio = CalendarioUtils.getCalendario();
		calendarInicio.setTime(dataInicio);
		calendarInicio.add(Calendar.HOUR, 3);

		Calendar calendarFim = CalendarioUtils.getCalendario();
		calendarFim.setTime(dataFim);
		calendarFim.add(Calendar.HOUR, 3);

		query = new SolrQuery();
		query.setQuery("*:*");
		query.setRows(1000);
		String busca = recuperaFontes(fontes);
		busca += removeIrrelevantes(nome);
		busca = "tstamp:[" + formatadorDeData.format(calendarInicio.getTime())
				+ " TO " + formatadorDeData.format(calendarFim.getTime())
				+ "] tipo:\"twitter\"";
		query.set("fq", busca);

		try {
			QueryResponse rsp = server.query(query, METHOD.POST);

			if (rsp != null) {

				for (SolrDocument documento : rsp.getResults()) {
					Documento doc = new Documento(documento, nome);
					retorno.add(doc);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return retorno;

	}

	public List<Documento> recuperaDocumentosTortaOpiniao(String nome,
			String orientacao, Date dataInicio, Date dataFim,
			List<String> fontes) {
		List<Documento> retorno = new ArrayList<Documento>();

		Calendar calendarInicio = CalendarioUtils.getCalendario();
		calendarInicio.setTime(dataInicio);
		calendarInicio.add(Calendar.HOUR, 3);

		Calendar calendarFim = CalendarioUtils.getCalendario();
		calendarFim.setTime(dataFim);
		calendarFim.add(Calendar.HOUR, 3);

		query = new SolrQuery();
		query.setQuery("*:*");
		query.setRows(1000);
		String busca = recuperaFontes(fontes);
		busca += removeIrrelevantes(nome);
		busca += recuperaOrientacao(nome, orientacao);
		busca += " tstamp:["
				+ formatadorDeData.format(calendarInicio.getTime()) + " TO "
				+ formatadorDeData.format(calendarFim.getTime()) + "] ";
		query.set("fq", busca);

		try {
			QueryResponse rsp = server.query(query, METHOD.POST);

			if (rsp != null) {

				for (SolrDocument documento : rsp.getResults()) {
					Documento doc = new Documento(documento, nome, orientacao);

					retorno.add(doc);

				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return retorno;

	}

	public List<Documento> recuperaDocumentosLinhaOpiniao(String nome,
			Date data, String orientacao, List<String> fontes) {
		List<Documento> retorno = new ArrayList<Documento>();

		Calendar calendar = CalendarioUtils.getCalendario();
		calendar.setTime(data);
		calendar.add(Calendar.HOUR, 3);

		query = new SolrQuery();
		query.setQuery("*:*");
		query.setRows(1000);
		String tmstamp = "tstamp:["
				+ formatadorDeData.format(calendar.getTime()) + " TO ";
		calendar.add(Calendar.DAY_OF_MONTH, 1);
		tmstamp += formatadorDeData.format(calendar.getTime()) + "]";
		String busca = recuperaFontes(fontes);
		busca += removeIrrelevantes(nome);
		busca += recuperaOrientacao(nome, orientacao);
		busca += tmstamp;
		query.set("fq", busca);

		try {
			QueryResponse rsp = server.query(query, METHOD.POST);

			if (rsp != null) {

				for (SolrDocument documento : rsp.getResults()) {

					Documento doc = new Documento(documento, nome, orientacao);

					retorno.add(doc);

				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return retorno;
	}

	public List<Documento> recuperaDocumentosLinhaMencao(String nome,
			Date data, List<String> fontes) {
		List<Documento> retorno = new ArrayList<Documento>();

		Calendar calendar = CalendarioUtils.getCalendario();
		calendar.setTime(data);
		calendar.add(Calendar.HOUR, 3);

		query = new SolrQuery();
		query.setQuery("*:*");
		query.setRows(1000);
		String tmstamp = "tstamp:["
				+ formatadorDeData.format(calendar.getTime()) + " TO ";
		calendar.add(Calendar.DAY_OF_MONTH, 1);
		tmstamp += formatadorDeData.format(calendar.getTime()) + "]";
		String busca = recuperaFontes(fontes);
		busca += removeIrrelevantes(nome);
		busca += tmstamp;
		query.set("fq", busca);

		try {
			QueryResponse rsp = server.query(query, METHOD.POST);

			if (rsp != null) {

				for (SolrDocument documento : rsp.getResults()) {
					Documento doc = new Documento(documento, nome);

					retorno.add(doc);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return retorno;
	}

	public List<Documento> recuperaDocumentosTagCloud(String nome,
			String palavra, Date dataInicio, Date dataFim, List<String> fontes) {
		List<Documento> retorno = new ArrayList<Documento>();

		Calendar calendarInicio = CalendarioUtils.getCalendario();
		calendarInicio.setTime(dataInicio);
		calendarInicio.add(Calendar.HOUR, 3);

		Calendar calendarFim = CalendarioUtils.getCalendario();
		calendarFim.setTime(dataFim);
		calendarFim.add(Calendar.HOUR, 3);

		query = new SolrQuery();
		query.setQuery(palavra.toLowerCase());
		query.setRows(1000);
		// query.addHighlightField("filteredContent");
		// query.setHighlight(true);
		// query.setHighlightFragsize(300);
		// query.setHighlightSimplePre("<b>");
		// query.setHighlightSimplePost("</b>");
		query.set(
				"fq",
				"-orientacao:\"" + nome.toLowerCase()
						+ ":irrelevante\" mencionados:\"" + nome.toLowerCase()
						+ "\" tstamp:["
						+ formatadorDeData.format(calendarInicio.getTime())
						+ " TO "
						+ formatadorDeData.format(calendarFim.getTime()) + "]");

		try {
			QueryResponse rsp = server.query(query, METHOD.POST);

			if (rsp != null) {

				for (SolrDocument documento : rsp.getResults()) {
					Documento doc = new Documento(documento, nome);

					retorno.add(doc);

				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return retorno;
	}

	public Documento recuperaDocumentoEntidade(String nome) {
		query = new SolrQuery();
		query.setQuery("*:*");
		query.setRows(500);
		String busca = removeIrrelevantes(nome);
		query.set("fq", busca);

		try {
			Random rand = new Random();

			QueryResponse rsp = server.query(query, METHOD.POST);
			SolrDocument documento = rsp.getResults().get(
					rand.nextInt(rsp.getResults().size()));
			return new Documento(documento, nome);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}

	@SuppressWarnings("unchecked")
	public void salvarRotulacaoManual(String solrId,
			OrientacaoClassificador orientacao) {
		query = new SolrQuery();
		query.setQuery("*:*");
		query.set("fq", "id:" + solrId);
		try {
			QueryResponse rsp = server.query(query, METHOD.POST);
			SolrDocument documento = rsp.getResults().get(0);
			List<String> orientacoes;
			if (documento.containsKey("classificacoesManuais")) {
				orientacoes = (List<String>) documento
						.getFieldValue("classificacoesManuais");
			} else {
				orientacoes = new ArrayList<String>();
			}
			orientacoes.add(orientacao.toString().toLowerCase());
			documento.removeFields("classificacoesManuais");
			documento.addField("classificacoesManuais", orientacoes);

			server.add(toSolrInputDocument(documento));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private static SolrInputDocument toSolrInputDocument(SolrDocument documento) {
		SolrInputDocument doc = new SolrInputDocument();
		for (String name : documento.getFieldNames()) {
			doc.addField(name, documento.getFieldValue(name));
		}
		return doc;
	}

	public Map<String, Integer> recuperaTermos(String nome, Date dataInicio,
			Date dataFim, List<String> fontes) {
		Map<String, Integer> termos = new HashMap<String, Integer>();

		Calendar calendarInicio = CalendarioUtils.getCalendario();
		calendarInicio.setTime(dataInicio);
		calendarInicio.add(Calendar.HOUR, 3);

		Calendar calendarFim = CalendarioUtils.getCalendario();
		calendarFim.setTime(dataFim);
		calendarFim.add(Calendar.HOUR, 3);

		query = new SolrQuery();
		query.setQuery("*:*");
		query.set("facet", true);
		String busca = recuperaFontes(fontes);
		busca = "tstamp:[" + formatadorDeData.format(calendarInicio.getTime())
				+ " TO " + formatadorDeData.format(calendarFim.getTime()) + "]";
		query.set("facet.field", "filteredContent");
		query.set("facet.limit", 100);
		query.set("facet.sort", "count");
		query.set("fq", busca);
		try {
			QueryResponse rsp = server.query(query, METHOD.POST);

			if (rsp != null) {
				for (Count count : rsp.getFacetFields().get(0).getValues()) {
					termos.put(count.getName(), (int) count.getCount());
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

		return termos;
	}

	public List<Integer> recuperaMencoes(String nome, Date dataInicio,
			Date dataFim, List<String> fontes) {
		List<Integer> mencoes = new ArrayList<Integer>();

		Calendar calendarInicio = CalendarioUtils.getCalendario();
		calendarInicio.setTime(dataInicio);
		calendarInicio.add(Calendar.HOUR, 3);

		Calendar calendarFim = CalendarioUtils.getCalendario();
		calendarFim.setTime(dataFim);
		calendarFim.add(Calendar.HOUR, 3);
		
		List<Integer> positivos = recuperaContagemDocumentosPositivos(nome, dataInicio, dataFim, fontes);
		List<Integer> neutros = recuperaContagemDocumentosNeutros(nome, dataInicio, dataFim, fontes);
		List<Integer> negativos = recuperaContagemDocumentosNegativos(nome, dataInicio, dataFim, fontes);
		
		for (int i = 0; i < positivos.size(); i++){
			mencoes.add(positivos.get(i)+neutros.get(i)+negativos.get(i));
		}
		
		return mencoes;
	}

	public Map<Orientacao, List<Double>> recuperaOrientacaoPonderada(
			String nome, Orientacao orientacao, Date dataInicio, Date dataFim,
			List<String> fontes) {

		Calendar calendarInicio = CalendarioUtils.getCalendario();
		calendarInicio.setTime(dataInicio);
		calendarInicio.add(Calendar.HOUR, 3);

		Calendar calendarFim = CalendarioUtils.getCalendario();
		calendarFim.setTime(dataFim);
		calendarFim.add(Calendar.HOUR, 3);

		Map<Orientacao, List<Double>> orientacoesPonderadas = new HashMap<Orientacao, List<Double>>();
		List<Double> hits = new ArrayList<Double>();

		query = new SolrQuery();
		query.setQuery("*:*");
		String busca = recuperaFontes(fontes);
		busca += removeIrrelevantes(nome);
		busca += recuperaOrientacao(nome, orientacao);
		busca += " tstamp:["
				+ formatadorDeData.format(calendarInicio.getTime()) + " TO "
				+ formatadorDeData.format(calendarFim.getTime()) + "] ";
		query.set("fq", busca);
		query.set("facet.date", "tstamp");
		query.set("facet.date.end",
				formatadorDeData.format(calendarFim.getTime()));
		query.set("facet.date.start",
				formatadorDeData.format(calendarInicio.getTime()));
		query.set("stats", "true");
		query.set("stats.field", "certeza");

		try {
			QueryResponse rsp = server.query(query, METHOD.POST);

			if (rsp != null) {
				double hit = rsp.getFieldStatsInfo().get("certeza").getSum();
				hits.add(hit);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		orientacoesPonderadas.put(orientacao, hits);
		return orientacoesPonderadas;
	}

	public Map<Orientacao, List<Integer>> recuperaOrientacao(String nome,
			Orientacao orientacao, Date dataInicio, Date dataFim,
			List<String> fontes) {

		Calendar calendarInicio = CalendarioUtils.getCalendario();
		calendarInicio.setTime(dataInicio);
		calendarInicio.add(Calendar.HOUR, 3);

		Calendar calendarFim = CalendarioUtils.getCalendario();
		calendarFim.setTime(dataFim);
		calendarFim.add(Calendar.HOUR, 3);

		Map<Orientacao, List<Integer>> orientacoes = new HashMap<Orientacao, List<Integer>>();

		orientacoes.put(orientacao, recuperaContagemDocumentos(nome,
				orientacao, dataInicio, dataFim,
				fontes));
		return orientacoes;
	}

	private String recuperaFontes(List<String> fontes) {
		String busca = "";
		if (!fontes.isEmpty()) {
			busca += "(";
		}
		for (String fonte : fontes) {
			busca += "tipo:\"" + removeAcentos(fonte.toLowerCase()) + "\" ";

			if (fontes.indexOf(fonte) < (fontes.size() - 1)) {
				busca += " OR ";
			} else {
				busca += ")";
			}
		}
		return busca;
	}

	private String recuperaOrientacao(String nome, Orientacao orientacao) {
		return recuperaOrientacao(nome, orientacao.toString());
	}

	private String recuperaOrientacao(String nome, String orientacao) {
		String busca = "ParMencionadoOrientacaoLM:\"" + nome.toLowerCase()
				+ ":" + orientacao.toLowerCase() + "\"";
		return busca;
	}

	private String removeIrrelevantes(String nome) {
		String busca = " mencionados:\"" + nome.toLowerCase()
				+ "\" -classificacoesManuais:\"" + nome.toLowerCase()
				+ ":irrelevante\" -ParMencionadoOrientacaoLM:\""
				+ nome.toLowerCase() + ":irrelevante\" ";
		return busca;
	}

	private List<Integer> recuperaContagemDocumentosPositivos(String nome,
			Date dataInicio, Date dataFim, List<String> fontes) {
		return recuperaContagemDocumentos(nome,
				Orientacao.POSITIVO, dataInicio, dataFim, fontes);
	}

	private List<Integer> recuperaContagemDocumentosNeutros(String nome,
			Date dataInicio, Date dataFim, List<String> fontes) {
		return recuperaContagemDocumentos(nome, Orientacao.NEUTRO,
				dataInicio, dataFim, fontes);
	}

	private List<Integer> recuperaContagemDocumentosNegativos(String nome,
			Date dataInicio, Date dataFim, List<String> fontes) {
		return recuperaContagemDocumentos(nome,
				Orientacao.NEGATIVO, dataInicio, dataFim, fontes);
	}
	
	private List<Integer> recuperaContagemDocumentos(String nome,
			Orientacao orientacao, Date dataInicio, Date dataFim,
			List<String> fontes) {

		Calendar calendarInicio = CalendarioUtils.getCalendario();
		calendarInicio.setTime(dataInicio);
		calendarInicio.add(Calendar.HOUR, 3);

		Calendar calendarFim = CalendarioUtils.getCalendario();
		calendarFim.setTime(dataFim);
		calendarFim.add(Calendar.HOUR, 3);

		List<Integer> hits = new ArrayList<Integer>();

		query = new SolrQuery();
		query.setQuery("*:*");
		query.set("facet", true);
		String busca = recuperaFontes(fontes);
		busca += recuperaOrientacao(nome, orientacao);
		busca += " tstamp:["
				+ formatadorDeData.format(calendarInicio.getTime()) + " TO "
				+ formatadorDeData.format(calendarFim.getTime()) + "] ";
		query.set("fq", busca);
		query.set("facet.date", "tstamp");
		query.set("facet.date.end",
				formatadorDeData.format(calendarFim.getTime()));
		query.set("facet.date.include", "all");
		query.set("facet.date.start",
				formatadorDeData.format(calendarInicio.getTime()));
		query.set("facet.date.gap", "+1DAY");

		try {

			QueryResponse rsp = server.query(query, METHOD.POST);

			if (rsp != null) {
				for (FacetField field : rsp.getFacetDates()) {
					for (Count count : field.getValues()) {
						hits.add((int) count.getCount());
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		return hits;
	}

	private static String removeAcentos(String str) {
		CharSequence cs = new StringBuilder(str == null ? "" : str);
		return Normalizer.normalize(cs, Normalizer.Form.NFKD).replaceAll(
				"\\p{InCombiningDiacriticalMarks}+", "");
	}

}
